/****************************************************************************
**
** Copyright (C) 2007-2007 Trolltech ASA. All rights reserved.
**
** This file is part of the Graphics Dojo project on Trolltech Labs.
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
#include "widget.h"

#include <QPainter>
#include <QBrush>
#include <QPen>
#include <QColor>

#include <QtDebug>

#include <cmath>


// Exponential blur, Jani Huhtanen, 2006
//
template<int aprec, int zprec>
static inline void blurinner(unsigned char *bptr, int &zR,
                             int &zG, int &zB, int &zA, int alpha);

template<int aprec,int zprec>
static inline void blurrow(QImage & im, int line, int alpha);

template<int aprec, int zprec>
static inline void blurcol(QImage & im, int col, int alpha);

/*
 *  expblur(QImage &img, int radius)
 *
 *  In-place blur of image 'img' with kernel
 *  of approximate radius 'radius'.
 *
 *  Blurs with two sided exponential impulse
 *  response.
 *
 *  aprec = precision of alpha parameter 
 *  in fixed-point format 0.aprec
 *
 *  zprec = precision of state parameters
 *  zR,zG,zB and zA in fp format 8.zprec
 */
template<int aprec,int zprec>
void expblur(QImage &img, int radius )
{
    if (radius < 1)
        return;

    /* Calculate the alpha such that 90% of 
       the kernel is within the radius.
       (Kernel extends to infinity) 
    */
    int alpha = (int)((1<<aprec)*(1.0f-expf(-2.3f/(radius+1.f))));

    for(int row=0;row<img.height();row++) {
        blurrow<aprec,zprec>(img,row,alpha);
    }

    for(int col=0;col<img.width();col++) {
        blurcol<aprec,zprec>(img,col,alpha);
    }
    return;
}

template<int aprec, int zprec>
static inline void blurinner(unsigned char *bptr,
                             int &zR, int &zG, int &zB, int &zA, int alpha)
{
    int R,G,B,A;
    R = *bptr;
    G = *(bptr+1);
    B = *(bptr+2);
    A = *(bptr+3);

    zR += (alpha * ((R<<zprec)-zR))>>aprec;
    zG += (alpha * ((G<<zprec)-zG))>>aprec;
    zB += (alpha * ((B<<zprec)-zB))>>aprec;
    zA += (alpha * ((A<<zprec)-zA))>>aprec;

    *bptr =     zR>>zprec;
    *(bptr+1) = zG>>zprec;
    *(bptr+2) = zB>>zprec;
    *(bptr+3) = zA>>zprec;
}

template<int aprec,int zprec>
static inline void blurrow( QImage & im, int line, int alpha)
{
    int zR,zG,zB,zA;

    QRgb *ptr = (QRgb *)im.scanLine(line);

    zR = *((unsigned char *)ptr    )<<zprec;
    zG = *((unsigned char *)ptr + 1)<<zprec;
    zB = *((unsigned char *)ptr + 2)<<zprec;
    zA = *((unsigned char *)ptr + 3)<<zprec;

    for(int index=1; index<im.width(); index++) {
        blurinner<aprec,zprec>((unsigned char *)&ptr[index],
                               zR, zG, zB, zA, alpha);
    }
    for(int index=im.width()-2; index>=0; index--) {
        blurinner<aprec,zprec>((unsigned char *)&ptr[index],
                               zR, zG, zB, zA, alpha);
    }
 

}

template<int aprec, int zprec>
static inline void blurcol(QImage & im, int col, int alpha)
{
    int zR,zG,zB,zA;

    QRgb *ptr = (QRgb *)im.bits();
    ptr+=col;

    zR = *((unsigned char *)ptr    )<<zprec;
    zG = *((unsigned char *)ptr + 1)<<zprec;
    zB = *((unsigned char *)ptr + 2)<<zprec;
    zA = *((unsigned char *)ptr + 3)<<zprec;

    for(int index=im.width(); index<(im.height()-1)*im.width();
        index+=im.width()) {
        blurinner<aprec,zprec>((unsigned char *)&ptr[index],
                               zR, zG, zB, zA, alpha);
    }

    for(int index=(im.height()-2)*im.width(); index>=0;
        index-=im.width()) {
        blurinner<aprec,zprec>((unsigned char *)&ptr[index],
                               zR, zG, zB, zA, alpha);
    }

}

Widget::Widget()
    : QWidget()
{
    m_tile = QPixmap(100, 100);
    m_tile.fill(Qt::white);
    QPainter pt(&m_tile);
    QColor color(240, 240, 240);
    pt.fillRect(0, 0, 50, 50, color);
    pt.fillRect(50, 50, 50, 50, color);
    pt.end();

    m_styleBox = new QComboBox(this);
    m_styleBox->addItem("kdelogo.svg");
    m_styleBox->addItem("butterfly.svg");
    m_styleBox->addItem("canon.svg");
    m_styleBox->addItem("elephant.svg");
    m_styleBox->addItem("hearts.svg");
    m_styleBox->addItem("troll.svg");

    m_radiusBox = new QSpinBox(this);
    m_radiusBox->setMinimum(0);
    m_radiusBox->setMaximum(40);
    m_radiusBox->setSingleStep(2);
    m_radiusBox->setValue(5);
        
    connect(m_styleBox, SIGNAL(activated(const QString &)),
            this, SLOT(loadFile(const QString &)));
    connect(m_radiusBox, SIGNAL(valueChanged(int)),
            this, SLOT(changeRadius(int)));

    loadFile(QLatin1String("kdelogo.svg"));
}

void Widget::paintEvent(QPaintEvent *e)
{
    QPainter p(this);
    p.setClipRect(e->rect());
    p.drawTiledPixmap(rect(), m_tile);
    
    p.setRenderHint(QPainter::Antialiasing);

    QPixmap pix(m_pixmap.size());
    pix.fill(Qt::black);

    QPixmap shadow = m_pixmap;
    QPainter px(&shadow);
    px.setCompositionMode(QPainter::CompositionMode_SourceAtop);
    px.drawPixmap(0, 0, pix);
    px.end();

    const QPoint svgPos(50, 50);
    const int radius = m_radiusBox->value();
    QImage img(shadow.width()+radius*2, shadow.height()+radius*2,
               QImage::Format_ARGB32_Premultiplied);
    px.begin(&img);
    px.drawPixmap(radius, radius, shadow);
    px.end();
    
    expblur<16,7>(img, m_radiusBox->value());
    p.drawImage(svgPos+QPoint(20, 20), img);
    p.drawPixmap(svgPos, m_pixmap);

    drawControl(&p);
}


QSize Widget::sizeHint() const
{
    return QSize(640, 480);
}

void Widget::loadFile(const QString &file)
{
    QString theme = QString(":/%1").arg(file);
    m_svgFile.load(theme);
    cacheFile();
    update();
}

void Widget::cacheFile()
{
    if (m_svgFile.isValid() && width() >= 140 && height() >= 140) {
        m_pixmap = QPixmap(width()-100, height()-100);
        m_pixmap.fill(Qt::transparent);
        QPainter p(&m_pixmap);
        p.setRenderHint(QPainter::Antialiasing);
        m_svgFile.render(&p);
        p.end();
    }
}

void Widget::resizeEvent(QResizeEvent *)
{
    cacheFile();    
    m_styleBox->setGeometry(65, 15,
                            105, 20);
    m_radiusBox->setGeometry(65, 40,
                             105, 20);
}

void Widget::drawControl(QPainter *p)
{
    QRect rect(10, 10,
               170, 60);
    p->setRenderHint(QPainter::Antialiasing);
    p->setPen(QPen(Qt::black));
    QLinearGradient grad(rect.topLeft(), rect.bottomLeft());
    grad.setColorAt(0, QColor(0, 0, 0, 127));
    grad.setColorAt(1, QColor(255, 255, 255, 127));
    p->setBrush(QBrush(grad));
    p->drawRoundRect(rect.x(), rect.y(), rect.width(), rect.height(),
                    20, 40);
    p->drawText(rect.x()+ 10,
                rect.y()+ 20, "File:");
    p->drawText(rect.x()+ 10,
                rect.y()+ 45, "Radius:");
}

void Widget::changeRadius(int)
{
    update();
}
